import { Plugin } from "vite";

interface PathsJsonPluginOptions {
  outDir?: string;
  name?: string;
}
const vimServiceWorker = async (options: PathsJsonPluginOptions, paths: { [s: string]: unknown; } | ArrayLike<unknown>) => {
  // 获取输出目录
  const distPath = options.outDir || "dist";
  // 创建并写入 service-worker.js 文件到输出目录
  const URLS_TO_CACHE = Object.values(paths);
  // 处理 service-worker.js
  const modifiedData = `
const CACHE_NAME = "${options.name || 'z-cache-v'}";
const URLS_TO_CACHE = ${JSON.stringify(URLS_TO_CACHE, null, 2)};
const _pathList = new Set();

// 默认第一次进行服务安装，并缓存默认的资源路径
self.addEventListener("install", function (event) {
  console.log("install...");
  event.waitUntil(
    caches.open(CACHE_NAME).then(function (cache) {
      return cache.addAll(URLS_TO_CACHE);
    })
  );
});

// 第二次进行请求拦截，并动态的获取新的资源路径。非脱机状态不拦截 html 文件
self.addEventListener("fetch", function (event) {
  const request = event.request;
  event.respondWith(
    caches.match(request).then(function (response) {
      if (response && response.status === 200) {
        // 如果是 html 文件，有网时拉新数据，断网时走缓存
        const contentType = response.headers.get("Content-Type");
        if (contentType && contentType.includes("text/html")) {
          if (navigator.onLine) {
            return fetch(event.request);
          } else {
            return response;
          }
        }
        return response;
      } else {
        const path = request.url.replace(location.origin, "");
        _pathList.add(path);
      }
      return fetch(event.request);
    })
  );
});

// 与外部页面交互，等待 serviceWorker 注册成功后，缓存新资源路径
self.addEventListener("message", async (event) => {
  console.log("收到外部页面消息", event.data, "开始缓存新的资源路径");
  if (event.data === "registerSuccess") {
    // 过滤 pathList 中已经缓存过的 path
    const list = await Promise.all(
      Array.from(_pathList).map(async (path) =>
        (await caches.match(path)) ? null : path
      )
    );
    // 把 pathList 添加到缓存中
    const newList = list.filter((path) => path);
    if (newList.length) {
      caches.open(CACHE_NAME).then((cache) => {
        console.log("cache...", [cache], cache.keys(), list);
        return cache.addAll(newList);
      });
      event.source.postMessage("完成新资源缓存任务");
    } else {
      event.source.postMessage("未发现新资源");
    }
  }
});

// 在激活事件中完成清理其他旧的缓存
self.addEventListener("activate", function (event) {
  console.log("activate...");
  event.waitUntil(
    // 删除旧的缓存
    caches.keys().then(function (cacheNames) {
      return Promise.all(
        cacheNames
          .filter(function (cacheName) {
            // 过滤出需要删除的缓存名称
            return (
              cacheName.startsWith("${options.name || 'z-cache-v'}") && cacheName !== CACHE_NAME
            );
          })
          .map(function (cacheName) {
            // 删除对应的缓存
            return caches.delete(cacheName);
          })
      );
    })
  );
});
  `;
  const { resolve } = await import("path");
  const { writeFile } = await import("fs/promises");
  const serviceWorkerPath = resolve(distPath, "service-worker.js");
  await writeFile(serviceWorkerPath, modifiedData);
};

const vimIndexHtml = `
    <script id="service-worker">
    // 注册 Service Worker
    if ("serviceWorker" in navigator) {
      window.addEventListener("load", function () {
        navigator.serviceWorker.register("./service-worker.js").then(
          function (registration) {
            console.log("Service Worker 注册成功:", [registration]);
            // 传递
            navigator.serviceWorker.controller &&
              navigator.serviceWorker.controller.postMessage("registerSuccess");
            // 接收
            navigator.serviceWorker.addEventListener("message", function (e) {
              console.log("收到 service worker 消息：", [e.data]);
            });
          },
          function (error) {
            console.log("Service Worker 注册失败:", [error]);
          }
        );
      });
    }
    </script>
  </head>
`;
export default function pathsJsonPlugin(
  options: PathsJsonPluginOptions = {}
): Plugin {
  let paths: { [key: string]: string } = {};
  return {
    name: "paths-json-plugin",
    enforce: "post",
    async generateBundle(_, bundle) {
      for (const filename of Object.keys(bundle)) {
        const file = bundle[filename];
        paths[filename] = file.fileName;
      }
    },
    async writeBundle() {
      vimServiceWorker(options, paths);
    },
    async closeBundle() {},
    transformIndexHtml: (html) => html.includes('id="service-worker"') ? html : html.replace("</head>", vimIndexHtml),
  };
}
